/*
 * Copyright (C) 2013, Pavel Samko <bulldozerbsg@gmail.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the "Pavel Samko" nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGE.
 */

#include <windows.h>
#include <stdlib.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <wchar.h>
#include <stdint.h>
#include <assert.h>
#include "wcxhead.h"
#include "plug_funcs.h"

#define PIPE_BUFF_SIZE (102400)

#define IMPORT(module, func) t##func _##func = (t##func)GetProcAddress(module, #func); \
	if (_##func) {func = _##func;}

BOOL s_loop = TRUE;
HANDLE s_pipe = INVALID_HANDLE_VALUE;
unsigned char s_buff[PIPE_BUFF_SIZE];
HMODULE s_plugIn = NULL;

void needExit(int sig)
{
	s_loop = FALSE;
}

// plugs
HANDLE __stdcall ImplOpenArchive(tOpenArchiveData *ArchiveData)
{
	if (ArchiveData)
	{
		ArchiveData->OpenResult = E_NOT_SUPPORTED;
	}
	return 0;
}

int __stdcall ImplReadHeader(HANDLE hArcData, tHeaderData *HeaderData)
{
	return E_NO_FILES;
}

int __stdcall ImplProcessFile(HANDLE hArcData, int Operation, char *DestPath, char *DestName)
{
	return 0;
}

int __stdcall ImplCloseArchive(HANDLE hArcData)
{
	return 0;
}

void __stdcall ImplSetChangeVolProc(HANDLE hArcData, tChangeVolProc pChangeVolProc)
{
}

void __stdcall ImplSetProcessDataProc(HANDLE hArcData, tProcessDataProc pProcessDataProc)
{
}

int __stdcall ImplPackFiles(char *PackedFile, char *SubPath, char *SrcPath, char *AddList, int Flags)
{
	return E_NOT_SUPPORTED;
}

int __stdcall ImplDeleteFiles(char *PackedFile, char *DeleteList)
{
	return E_NOT_SUPPORTED;
}

int __stdcall ImplGetPackerCaps(void)
{
	return 0;
}

void __stdcall ImplConfigurePacker(HWND Parent, HINSTANCE DllInstance)
{
}

int __stdcall ImplStartMemPack(int Options, char *FileName)
{
	return 0;
}

int __stdcall ImplPackToMem(int hMemPack, char *BufIn, int InLen, int *Taken, char *BufOut, int OutLen, int *Written, int SeekBy)
{
	return E_NOT_SUPPORTED;
}

int __stdcall ImplDoneMemPack(int hMemPack)
{
	return E_NOT_SUPPORTED;
}

BOOL __stdcall ImplCanYouHandleThisFile(char *FileName)
{
	return FALSE;
}

void __stdcall ImplPackSetDefaultParams(PackDefaultParamStruct *dps)
{
}

void __stdcall ImplPkSetCryptCallback(tPkCryptProc pPkCryptProc, int CryptoNr, int Flags)
{
}

int __stdcall ImplReadHeaderEx (HANDLE hArcData, tHeaderDataEx *HeaderDataEx)
{
	return E_NO_FILES;
}

int __stdcall ImplGetBackgroundFlags(void)
{
	return 0;
}

HANDLE __stdcall ImplOpenArchiveW(tOpenArchiveDataW *ArchiveData)
{
	if (ArchiveData)
	{
		ArchiveData->OpenResult = E_NOT_SUPPORTED;
	}
	return NULL;
}

int __stdcall ImplReadHeaderExW(HANDLE hArcData, tHeaderDataExW *HeaderDataEx)
{
	return E_NO_FILES;
}

int __stdcall ImplProcessFileW(HANDLE hArcData, int Operation, wchar_t *DestPath, wchar_t *DestName)
{
	return 0;
}

void __stdcall ImplSetChangeVolProcW(HANDLE hArcData, tChangeVolProcW pChangeVolProc)
{
}

void __stdcall ImplSetProcessDataProcW(HANDLE hArcData, tProcessDataProcW pProcessDataProc)
{
}

int __stdcall ImplPackFilesW(wchar_t *PackedFile, wchar_t *SubPath, wchar_t *SrcPath, wchar_t *AddList, int Flags)
{
	return E_NOT_SUPPORTED;
}

int __stdcall ImplDeleteFilesW(wchar_t *PackedFile, wchar_t *DeleteList)
{
	return E_NOT_SUPPORTED;
}

int __stdcall ImplStartMemPackW(int Options, wchar_t *FileName)
{
	return 0;
}

BOOL __stdcall ImplCanYouHandleThisFileW(wchar_t *FileName)
{
	return FALSE;
}

void __stdcall ImplPkSetCryptCallbackW(tPkCryptProcW pPkCryptProc, int CryptoNr, int Flags)
{
}

// import func types
typedef HANDLE (__stdcall *tOpenArchive)(tOpenArchiveData *ArchiveData);
typedef int (__stdcall *tReadHeader)(HANDLE hArcData, tHeaderData *HeaderData);
typedef int (__stdcall *tProcessFile)(HANDLE hArcData, int Operation, char *DestPath, char *DestName);
typedef int (__stdcall *tCloseArchive)(HANDLE hArcData);
typedef void (__stdcall *tSetChangeVolProc)(HANDLE hArcData, tChangeVolProc pChangeVolProc);
typedef void (__stdcall *tSetProcessDataProc)(HANDLE hArcData, tProcessDataProc pProcessDataProc);
typedef int (__stdcall *tPackFiles)(char *PackedFile, char *SubPath, char *SrcPath, char *AddList, int Flags);
typedef int (__stdcall *tDeleteFiles)(char *PackedFile, char *DeleteList);
typedef int (__stdcall *tGetPackerCaps)(void);
typedef void (__stdcall *tConfigurePacker)(HWND Parent, HINSTANCE DllInstance);
typedef int (__stdcall *tStartMemPack)(int Options, char *FileName);
typedef int (__stdcall *tPackToMem)(int hMemPack, char *BufIn, int InLen, int *Taken, char *BufOut, int OutLen, int *Written, int SeekBy);
typedef int (__stdcall *tDoneMemPack)(int hMemPack);
typedef BOOL (__stdcall *tCanYouHandleThisFile)(char *FileName);
typedef void (__stdcall *tPackSetDefaultParams)(PackDefaultParamStruct *dps);
typedef void (__stdcall *tPkSetCryptCallback)(tPkCryptProc pPkCryptProc, int CryptoNr, int Flags);
typedef int (__stdcall *tReadHeaderEx)(HANDLE hArcData, tHeaderDataEx *HeaderDataEx);
typedef int (__stdcall *tGetBackgroundFlags)(void);
typedef HANDLE (__stdcall *tOpenArchiveW)(tOpenArchiveDataW *ArchiveData);
typedef int (__stdcall *tReadHeaderExW)(HANDLE hArcData, tHeaderDataExW *HeaderDataEx);
typedef int (__stdcall *tProcessFileW)(HANDLE hArcData, int Operation, wchar_t *DestPath, wchar_t *DestName);
typedef void (__stdcall *tSetChangeVolProcW)(HANDLE hArcData, tChangeVolProcW pChangeVolProc);
typedef void (__stdcall *tSetProcessDataProcW)(HANDLE hArcData, tProcessDataProcW pProcessDataProc);
typedef int (__stdcall *tPackFilesW)(wchar_t *PackedFile, wchar_t *SubPath, wchar_t *SrcPath, wchar_t *AddList, int Flags);
typedef int (__stdcall *tDeleteFilesW)(wchar_t *PackedFile, wchar_t *DeleteList);
typedef int (__stdcall *tStartMemPackW)(int Options, wchar_t *FileName);
typedef BOOL (__stdcall *tCanYouHandleThisFileW)(wchar_t *FileName);
typedef void (__stdcall *tPkSetCryptCallbackW)(tPkCryptProcW pPkCryptProc, int CryptoNr, int Flags);

// import funcs
tOpenArchive OpenArchive = ImplOpenArchive;
tReadHeader ReadHeader = ImplReadHeader;
tProcessFile ProcessFile = ImplProcessFile;
tCloseArchive CloseArchive = ImplCloseArchive;
tSetChangeVolProc SetChangeVolProc = ImplSetChangeVolProc;
tSetProcessDataProc SetProcessDataProc = ImplSetProcessDataProc;
tPackFiles PackFiles = ImplPackFiles;
tDeleteFiles DeleteFiles = ImplDeleteFiles;
tGetPackerCaps GetPackerCaps = ImplGetPackerCaps;
tConfigurePacker ConfigurePacker = ImplConfigurePacker;
tStartMemPack StartMemPack = ImplStartMemPack;
tPackToMem PackToMem = ImplPackToMem;
tDoneMemPack DoneMemPack = ImplDoneMemPack;
tCanYouHandleThisFile CanYouHandleThisFile = ImplCanYouHandleThisFile;
tPackSetDefaultParams PackSetDefaultParams = ImplPackSetDefaultParams;
tPkSetCryptCallback PkSetCryptCallback = ImplPkSetCryptCallback;
tReadHeaderEx ReadHeaderEx = ImplReadHeaderEx;
tGetBackgroundFlags GetBackgroundFlags = ImplGetBackgroundFlags;
tOpenArchiveW OpenArchiveW = ImplOpenArchiveW;
tReadHeaderExW ReadHeaderExW = ImplReadHeaderExW;
tProcessFileW ProcessFileW = ImplProcessFileW;
tSetChangeVolProcW SetChangeVolProcW = ImplSetChangeVolProcW;
tSetProcessDataProcW SetProcessDataProcW = ImplSetProcessDataProcW;
tPackFilesW PackFilesW = ImplPackFilesW;
tDeleteFilesW DeleteFilesW = ImplDeleteFilesW;
tStartMemPackW StartMemPackW = ImplStartMemPackW;
tCanYouHandleThisFileW CanYouHandleThisFileW = ImplCanYouHandleThisFileW;
tPkSetCryptCallbackW PkSetCryptCallbackW = ImplPkSetCryptCallbackW;

int __stdcall ChangeVolProc(char *ArcName, int Mode)
{
	uint8_t *p = s_buff;
	SET_FUNC(p, CHANGEVOLPROC)
	SET_CALLTYPE(p, CALL_QUERY)
	SET_STR_A(p, ArcName)
	SET_INT(p, Mode)
	DWORD writeSize = p - s_buff;
	DWORD writedSize;
	assert(WriteFile(s_pipe, s_buff, writeSize, &writedSize, NULL) || writeSize == writedSize);
	DWORD readedSize;
	assert(ReadFile(s_pipe, s_buff, PIPE_BUFF_SIZE, &readedSize, NULL));
	p = s_buff;
	uint8_t func; GET_FUNC(p, func)
	uint8_t callType; GET_CALLTYPE(p, callType)
	assert(func == CHANGEVOLPROC && callType == CALL_ANSWER);
	int r; GET_INT(p, r)
	return r; 
}

int __stdcall ChangeVolProcW(WCHAR *ArcName, int Mode)
{
	uint8_t *p = s_buff;
	SET_FUNC(p, CHANGEVOLPROCW)
	SET_CALLTYPE(p, CALL_QUERY)
	SET_STR_W(p, ArcName)
	SET_INT(p, Mode)
	DWORD writeSize = p - s_buff;
	DWORD writedSize;
	assert(WriteFile(s_pipe, s_buff, writeSize, &writedSize, NULL) || writeSize == writedSize);
	DWORD readedSize;
	assert(ReadFile(s_pipe, s_buff, PIPE_BUFF_SIZE, &readedSize, NULL));
	p = s_buff;
	uint8_t func; GET_FUNC(p, func)
	uint8_t callType; GET_CALLTYPE(p, callType)
	assert(func == CHANGEVOLPROCW && callType == CALL_ANSWER);
	int r; GET_INT(p, r)
	return r; 
}

int __stdcall ProcessDataProc(char *FileName, int Size)
{
	uint8_t *p = s_buff;
	SET_FUNC(p, PROCESSDATAPROC)
	SET_CALLTYPE(p, CALL_QUERY)
	SET_STR_A(p, FileName)
	SET_INT(p, Size)
	DWORD writeSize = p - s_buff;
	DWORD writedSize;
	assert(WriteFile(s_pipe, s_buff, writeSize, &writedSize, NULL) || writeSize == writedSize);
	DWORD readedSize;
	assert(ReadFile(s_pipe, s_buff, PIPE_BUFF_SIZE, &readedSize, NULL));
	p = s_buff;
	uint8_t func; GET_FUNC(p, func)
	uint8_t callType; GET_CALLTYPE(p, callType)
	assert(func == PROCESSDATAPROC && callType == CALL_ANSWER);
	int r; GET_INT(p, r)
	return r;
}

int __stdcall ProcessDataProcW(WCHAR *FileName, int Size)
{
	uint8_t *p = s_buff;
	SET_FUNC(p, PROCESSDATAPROCW)
	SET_CALLTYPE(p, CALL_QUERY)
	SET_STR_W(p, FileName)
	SET_INT(p, Size)
	DWORD writeSize = p - s_buff;
	DWORD writedSize;
	assert(WriteFile(s_pipe, s_buff, writeSize, &writedSize, NULL) || writeSize == writedSize);
	DWORD readedSize;
	assert(ReadFile(s_pipe, s_buff, PIPE_BUFF_SIZE, &readedSize, NULL));
	p = s_buff;
	uint8_t func; GET_FUNC(p, func)
	uint8_t callType; GET_CALLTYPE(p, callType)
	assert(func == PROCESSDATAPROCW && callType == CALL_ANSWER);
	int r; GET_INT(p, r)
	return r;
}

int __stdcall PkCryptProc(int CryptoNr, int Mode, char *ArchiveName, char *Password, int maxlen)
{
	uint8_t *p = s_buff;
	SET_FUNC(p, PKCRYPTPROC)
	SET_CALLTYPE(p, CALL_QUERY)
	SET_INT(p, CryptoNr)
	SET_INT(p, Mode)
	SET_STR_A(p, ArchiveName)
	if (Mode != PK_CRYPT_LOAD_PASSWORD && 
		Mode != PK_CRYPT_LOAD_PASSWORD_NO_UI)
	{
		SET_STR_A(p, Password)
		SET_INT(p, maxlen)
	} else
	{
		SET_INT(p, maxlen)
	}
	DWORD writeSize = p - s_buff;
	DWORD writedSize;
	assert(WriteFile(s_pipe, s_buff, writeSize, &writedSize, NULL) || writeSize == writedSize);
	DWORD readedSize;
	assert(ReadFile(s_pipe, s_buff, PIPE_BUFF_SIZE, &readedSize, NULL));
	p = s_buff;
	uint8_t func; GET_FUNC(p, func)
	uint8_t callType; GET_CALLTYPE(p, callType)
	assert(func == PKCRYPTPROC && callType == CALL_ANSWER);
	if (Mode == PK_CRYPT_LOAD_PASSWORD || 
		Mode == PK_CRYPT_LOAD_PASSWORD_NO_UI)
	{
		GET_BUFF2(p, Password)
	}
	int r; GET_INT(p, r)
	return r;
}

int __stdcall PkCryptProcW(int CryptoNr, int Mode, WCHAR *ArchiveName, WCHAR *Password, int maxlen)
{
	uint8_t *p = s_buff;
	SET_FUNC(p, PKCRYPTPROC)
	SET_CALLTYPE(p, CALL_QUERY)
	SET_INT(p, CryptoNr)
	SET_INT(p, Mode)
	SET_STR_W(p, ArchiveName)
	if (Mode != PK_CRYPT_LOAD_PASSWORD && 
		Mode != PK_CRYPT_LOAD_PASSWORD_NO_UI)
	{
		SET_STR_W(p, Password)
		SET_INT(p, maxlen)
	} else
	{
		SET_INT(p, maxlen)
	}
	DWORD writeSize = p - s_buff;
	DWORD writedSize;
	assert(WriteFile(s_pipe, s_buff, writeSize, &writedSize, NULL) || writeSize == writedSize);
	DWORD readedSize;
	assert(ReadFile(s_pipe, s_buff, PIPE_BUFF_SIZE, &readedSize, NULL));
	p = s_buff;
	uint8_t func; GET_FUNC(p, func)
	uint8_t callType; GET_CALLTYPE(p, callType)
	assert(func == PKCRYPTPROC && callType == CALL_ANSWER);
	if (Mode == PK_CRYPT_LOAD_PASSWORD || 
		Mode == PK_CRYPT_LOAD_PASSWORD_NO_UI)
	{
		GET_BUFF2(p, Password)
	}
	int r; GET_INT(p, r)
	return r;
}

int main(int argc, char *argv[])
{
	signal(SIGTERM, needExit);
	signal(SIGABRT, needExit);

	// get pipe name and plugin type
	if (argc != 3)
	{
		return -1;
	}
	char *pipeName = argv[2];
	char *pluginExt = argv[1];
	// connect to pipe
	s_pipe = CreateFile(pipeName,
		GENERIC_READ | GENERIC_WRITE,
		0,
		NULL,
		OPEN_EXISTING,
		0,
		NULL);
	if (s_pipe == INVALID_HANDLE_VALUE)
	{
		return -2;
	}
	DWORD mode = PIPE_READMODE_MESSAGE | PIPE_WAIT;
	if (!SetNamedPipeHandleState(s_pipe, &mode, NULL, NULL))
	{
		return -3;
	}
	// load plugin
	{
		char bridgeApp[MAX_PATH] = {0};
		strcpy(bridgeApp, argv[0]);
		char *lastDot = strrchr(bridgeApp, '.');
		if (lastDot)
		{
			lastDot[0] = 0;
		}
		char plugInDll[MAX_PATH];
		snprintf(plugInDll, MAX_PATH, "%s.%s", bridgeApp, pluginExt);
		s_plugIn = LoadLibraryA(plugInDll);
		if (!s_plugIn)
		{
			return -4;
		}
	}

	if (!_stricmp(pluginExt, "wcx"))
	{
		IMPORT(s_plugIn, OpenArchive)
		IMPORT(s_plugIn, ReadHeader)
		IMPORT(s_plugIn, ProcessFile)
		IMPORT(s_plugIn, CloseArchive)
		IMPORT(s_plugIn, SetChangeVolProc)
		IMPORT(s_plugIn, SetProcessDataProc)
		IMPORT(s_plugIn, PackFiles)
		IMPORT(s_plugIn, DeleteFiles)
		IMPORT(s_plugIn, GetPackerCaps)
		IMPORT(s_plugIn, ConfigurePacker)
		IMPORT(s_plugIn, StartMemPack)
		IMPORT(s_plugIn, PackToMem)
		IMPORT(s_plugIn, DoneMemPack)
		IMPORT(s_plugIn, CanYouHandleThisFile)
		IMPORT(s_plugIn, PackSetDefaultParams)
		IMPORT(s_plugIn, PkSetCryptCallback)
		IMPORT(s_plugIn, ReadHeaderEx)
		IMPORT(s_plugIn, GetBackgroundFlags)
		IMPORT(s_plugIn, OpenArchiveW)
		IMPORT(s_plugIn, ReadHeaderExW)
		IMPORT(s_plugIn, ProcessFileW)
		IMPORT(s_plugIn, SetChangeVolProcW)
		IMPORT(s_plugIn, SetProcessDataProcW)
		IMPORT(s_plugIn, PackFilesW)
		IMPORT(s_plugIn, DeleteFilesW)
		IMPORT(s_plugIn, StartMemPackW)
		IMPORT(s_plugIn, CanYouHandleThisFileW)
		IMPORT(s_plugIn, PkSetCryptCallbackW)
	} else
	if (!_stricmp(pluginExt, "wfx"))
	{
	} else
	if (!_stricmp(pluginExt, "wlx"))
	{
	} else
	if (!_stricmp(pluginExt, "wdx"))
	{
	} else
	{
		return -6;
	}

	while (s_loop)
	{
		DWORD readedSize;
		if (ReadFile(s_pipe, s_buff, PIPE_BUFF_SIZE, &readedSize, NULL))
		{
			// deserialize, process, serialize
			uint8_t *p = s_buff;
			uint8_t func; GET_FUNC(p, func)
			uint8_t callType; GET_CALLTYPE(p, callType)
			assert(callType == CALL_QUERY);
			if (func == OPENARCHIVE)
			{
				tOpenArchiveData openArchiveData = {0};
				GET_STR_A(p, openArchiveData.ArcName)
				GET_INT(p, openArchiveData.OpenMode)
				HANDLE r  = OpenArchive(&openArchiveData);
				p = s_buff;
				SET_FUNC(p, OPENARCHIVE)
				SET_CALLTYPE(p, CALL_ANSWER)
				SET_INT(p, openArchiveData.OpenResult)
				SET_HANDLE(p, r)
			} else
			if (func == READHEADER)
			{
				tHeaderData headerData = {0};
				HANDLE hArcData; GET_HANDLE(p, hArcData)
				int r = ReadHeader(hArcData, &headerData);
				p = s_buff;
				SET_FUNC(p, READHEADER)
				SET_CALLTYPE(p, CALL_ANSWER)
				SET_STR_A(p, headerData.ArcName)
				SET_STR_A(p, headerData.FileName)
				SET_INT(p, headerData.Flags)
				SET_INT(p, headerData.PackSize)
				SET_INT(p, headerData.UnpSize)
				SET_INT(p, headerData.HostOS)
				SET_INT(p, headerData.FileCRC)
				SET_INT(p, headerData.FileTime)
				SET_INT(p, headerData.UnpVer)
				SET_INT(p, headerData.Method)
				SET_INT(p, headerData.FileAttr)
				SET_INT(p, r)
			} else
			if (func == PROCESSFILE)
			{
				HANDLE hArcData; GET_HANDLE(p, hArcData)
				int Operation; GET_INT(p, Operation)
				char *DestPath; GET_STR_A(p, DestPath)
				char *DestName; GET_STR_A(p, DestName)
				int r = ProcessFile(hArcData, Operation, DestPath, DestName);
				p = s_buff;
				SET_FUNC(p, PROCESSFILE)
				SET_CALLTYPE(p, CALL_ANSWER)
				SET_INT(p, r)
			} else
			if (func == CLOSEARCHIVE)
			{
				HANDLE hArcData; GET_HANDLE(p, hArcData)
				int r = CloseArchive(hArcData);
				p = s_buff;
				SET_FUNC(p, CLOSEARCHIVE)
				SET_CALLTYPE(p, CALL_ANSWER)
				SET_INT(p, r)
			} else
			if (func == SETCHANGEVOLPROC)
			{
				HANDLE hArcData; GET_HANDLE(p, hArcData)
				SetChangeVolProc(hArcData, ChangeVolProc);
				p = s_buff;
				SET_FUNC(p, SETCHANGEVOLPROC)
				SET_CALLTYPE(p, CALL_ANSWER)
			} else
			if (func == SETPROCESSDATAPROC)
			{
				HANDLE hArcData; GET_HANDLE(p, hArcData)
				SetProcessDataProc(hArcData, ProcessDataProc);
				p = s_buff;
				SET_FUNC(p, SETPROCESSDATAPROC)
				SET_CALLTYPE(p, CALL_ANSWER)
			} else
			if (func == PACKFILES)
			{
				char *PackedFile; GET_STR_A(p, PackedFile)
				char *SubPath; GET_STR_A(p, SubPath)
				char *SrcPath; GET_STR_A(p, SrcPath)
				char *AddList; GET_STRLIST_A(p, AddList)
				int Flags; GET_INT(p, Flags)
				int r = PackFiles(PackedFile, SubPath, SrcPath, AddList, Flags);
				p = s_buff;
				SET_FUNC(p, PACKFILES)
				SET_CALLTYPE(p, CALL_ANSWER)
				SET_INT(p, r)
			} else
			if (func == DELETEFILES)
			{
				char *PackedFile; GET_STR_A(p, PackedFile)
				char *DeleteList; GET_STRLIST_A(p, DeleteList)
				int r = DeleteFiles(PackedFile, DeleteList);
				p = s_buff;
				SET_FUNC(p, DELETEFILES)
				SET_CALLTYPE(p, CALL_ANSWER)
				SET_INT(p, r)
			} else
			if (func == GETPACKERCAPS)
			{
				int r = GetPackerCaps();
				p = s_buff;
				SET_FUNC(p, GETPACKERCAPS)
				SET_CALLTYPE(p, CALL_ANSWER)
				SET_INT(p, r)
			} else
			if (func == CONFIGUREPACKER)
			{
				/// TODO: recv HWND
				HWND Parent = NULL;
				HINSTANCE DllInstance = s_plugIn;
				ConfigurePacker(Parent, DllInstance);
				p = s_buff;
				SET_FUNC(p, CONFIGUREPACKER)
				SET_CALLTYPE(p, CALL_ANSWER)
			} else
			if (func == STARTMEMPACK)
			{
				int Options; GET_INT(p, Options)
				char *FileName; GET_STR_A(p, FileName)
				int r = StartMemPack(Options, FileName);
				p = s_buff;
				SET_FUNC(p, STARTMEMPACK)
				SET_CALLTYPE(p, CALL_ANSWER)
				SET_INT(p, r)
			} else
			if (func == PACKTOMEM)
			{
				int hMemPack; GET_INT(p, hMemPack)
				int InLen;
				char *BufIn; GET_BUFF(p, BufIn, InLen)
				int Taken; GET_INT(p, Taken)
				int OutLen;
				char *BufOut; GET_BUFF(p, BufOut, OutLen)
				int Written; GET_INT(p, Written)
				int SeekBy; GET_INT(p, SeekBy)
				// copy out buff
				char *BufOutCopy = (char *)malloc(OutLen * sizeof(char));
				assert(BufOutCopy);
				memcpy(BufOutCopy, BufOut, OutLen * sizeof(char));
				int r = PackToMem(hMemPack, BufIn, InLen, &Taken, BufOutCopy, OutLen, &Written, SeekBy);
				p = s_buff;
				SET_FUNC(p, PACKTOMEM)
				SET_CALLTYPE(p, CALL_ANSWER)
				SET_INT(p, Taken)
				SET_BUFF(p, BufOutCopy, OutLen)
				SET_INT(p, Written)
				SET_INT(p, r)
				free(BufOutCopy);
			} else
			if (func == DONEMEMPACK)
			{
				int hMemPack; GET_INT(p, hMemPack)
				int r = DoneMemPack(hMemPack);
				p = s_buff;
				SET_FUNC(p, DONEMEMPACK)
				SET_CALLTYPE(p, CALL_ANSWER)
				SET_INT(p, r)
			} else
			if (func == CANYOUHANDLETHISFILE)
			{
				char *FileName; GET_STR_A(p, FileName)
				BOOL r = CanYouHandleThisFile(FileName);
				p = s_buff;
				SET_FUNC(p, CANYOUHANDLETHISFILE)
				SET_CALLTYPE(p, CALL_ANSWER)
				SET_BOOL(p, r)
			} else
			if (func == PACKSETDEFAULTPARAMS)
			{
				PackDefaultParamStruct dps = {0};
				GET_INT(p, dps.size)
				GET_DWORD(p, dps.PluginInterfaceVersionLow)
				GET_DWORD(p, dps.PluginInterfaceVersionHi)
				char *DefaultIniName; GET_STR_A(p, DefaultIniName)
				if (DefaultIniName)
				{
					strcpy(dps.DefaultIniName, DefaultIniName);
				}
				PackSetDefaultParams(&dps);
				p = s_buff;
				SET_FUNC(p, PACKSETDEFAULTPARAMS)
				SET_CALLTYPE(p, CALL_ANSWER)
			} else
			if (func == PKSETCRYPTCALLBACK)
			{
				int CryptoNr; GET_INT(p, CryptoNr)
				int Flags; GET_INT(p, Flags)
				PkSetCryptCallback(PkCryptProc, CryptoNr, Flags);
				p = s_buff;
				SET_FUNC(p, PKSETCRYPTCALLBACK)
				SET_CALLTYPE(p, CALL_ANSWER)
			} else
			if (func == READHEADEREX)
			{
				HANDLE hArcData; GET_HANDLE(p, hArcData)
				tHeaderDataEx HeaderDataEx = {0};
				int r = ReadHeaderEx(hArcData, &HeaderDataEx);
				p = s_buff;
				SET_FUNC(p, READHEADEREX)
				SET_CALLTYPE(p, CALL_ANSWER)
				SET_STR_A(p, HeaderDataEx.ArcName)
				SET_STR_A(p, HeaderDataEx.FileName)
				SET_INT(p, HeaderDataEx.Flags)
				SET_UINT(p, HeaderDataEx.PackSize)
				SET_UINT(p, HeaderDataEx.PackSizeHigh)
				SET_UINT(p, HeaderDataEx.UnpSize)
				SET_UINT(p, HeaderDataEx.UnpSizeHigh)
				SET_INT(p, HeaderDataEx.HostOS)
				SET_INT(p, HeaderDataEx.FileCRC)
				SET_INT(p, HeaderDataEx.FileTime)
				SET_INT(p, HeaderDataEx.UnpVer)
				SET_INT(p, HeaderDataEx.Method)
				SET_INT(p, HeaderDataEx.FileAttr)
				SET_INT(p, r)
			} else
			if (func == GETBACKGROUNDFLAGS)
			{
				int r = GetBackgroundFlags();
				p = s_buff;
				SET_FUNC(p, GETBACKGROUNDFLAGS)
				SET_CALLTYPE(p, CALL_ANSWER)
				SET_INT(p, r)
			} else
			/// TODO +W variant funcs
			if (func == OPENARCHIVEW)
			{
				tOpenArchiveDataW openArchiveData = {0};
				GET_STR_W(p, openArchiveData.ArcName)
				GET_INT(p, openArchiveData.OpenMode)
				HANDLE r  = OpenArchiveW(&openArchiveData);
				p = s_buff;
				SET_FUNC(p, OPENARCHIVEW)
				SET_CALLTYPE(p, CALL_ANSWER)
				SET_INT(p, openArchiveData.OpenResult)
				SET_HANDLE(p, r)
			} else
			if (func == READHEADEREXW)
			{
				HANDLE hArcData; GET_HANDLE(p, hArcData)
				tHeaderDataExW HeaderDataEx = {0};
				int r = ReadHeaderExW(hArcData, &HeaderDataEx);
				p = s_buff;
				SET_FUNC(p, READHEADEREXW)
				SET_CALLTYPE(p, CALL_ANSWER)
				SET_STR_W(p, HeaderDataEx.ArcName)
				SET_STR_W(p, HeaderDataEx.FileName)
				SET_INT(p, HeaderDataEx.Flags)
				SET_UINT(p, HeaderDataEx.PackSize)
				SET_UINT(p, HeaderDataEx.PackSizeHigh)
				SET_UINT(p, HeaderDataEx.UnpSize)
				SET_UINT(p, HeaderDataEx.UnpSizeHigh)
				SET_INT(p, HeaderDataEx.HostOS)
				SET_INT(p, HeaderDataEx.FileCRC)
				SET_INT(p, HeaderDataEx.FileTime)
				SET_INT(p, HeaderDataEx.UnpVer)
				SET_INT(p, HeaderDataEx.Method)
				SET_INT(p, HeaderDataEx.FileAttr)
				SET_INT(p, r)
			} else
			if (func == PROCESSFILEW)
			{
				HANDLE hArcData; GET_HANDLE(p, hArcData)
				int Operation; GET_INT(p, Operation)
				wchar_t *DestPath; GET_STR_W(p, DestPath)
				wchar_t *DestName; GET_STR_W(p, DestName)
				int r = ProcessFileW(hArcData, Operation, DestPath, DestName);
				p = s_buff;
				SET_FUNC(p, PROCESSFILEW)
				SET_CALLTYPE(p, CALL_ANSWER)
				SET_INT(p, r)
			} else
			if (func == SETCHANGEVOLPROCW)
			{
				HANDLE hArcData; GET_HANDLE(p, hArcData)
				SetChangeVolProcW(hArcData, ChangeVolProcW);
				p = s_buff;
				SET_FUNC(p, SETCHANGEVOLPROCW)
				SET_CALLTYPE(p, CALL_ANSWER)
			} else
			if (func == SETPROCESSDATAPROCW)
			{
				HANDLE hArcData; GET_HANDLE(p, hArcData)
				SetProcessDataProcW(hArcData, ProcessDataProcW);
				p = s_buff;
				SET_FUNC(p, SETPROCESSDATAPROCW)
				SET_CALLTYPE(p, CALL_ANSWER)
			} else
			if (func == PACKFILESW)
			{
				wchar_t *PackedFile; GET_STR_W(p, PackedFile)
				wchar_t *SubPath; GET_STR_W(p, SubPath)
				wchar_t *SrcPath; GET_STR_W(p, SrcPath)
				wchar_t *AddList; GET_STRLIST_W(p, AddList)
				int Flags; GET_INT(p, Flags)
				int r = PackFilesW(PackedFile, SubPath, SrcPath, AddList, Flags);
				p = s_buff;
				SET_FUNC(p, PACKFILESW)
				SET_CALLTYPE(p, CALL_ANSWER)
				SET_INT(p, r)
			} else
			if (func == DELETEFILESW)
			{
				wchar_t *PackedFile; GET_STR_W(p, PackedFile)
				wchar_t *DeleteList; GET_STRLIST_W(p, DeleteList)
				int r = DeleteFilesW(PackedFile, DeleteList);
				p = s_buff;
				SET_FUNC(p, DELETEFILESW)
				SET_CALLTYPE(p, CALL_ANSWER)
				SET_INT(p, r)
			} else
			if (func == STARTMEMPACKW)
			{
				int Options; GET_INT(p, Options)
				wchar_t *FileName; GET_STR_W(p, FileName)
				int r = StartMemPackW(Options, FileName);
				p = s_buff;
				SET_FUNC(p, STARTMEMPACKW)
				SET_CALLTYPE(p, CALL_ANSWER)
				SET_INT(p, r)
			} else
			if (func == CANYOUHANDLETHISFILEW)
			{
				wchar_t *FileName; GET_STR_W(p, FileName)
				BOOL r = CanYouHandleThisFileW(FileName);
				p = s_buff;
				SET_FUNC(p, CANYOUHANDLETHISFILEW)
				SET_CALLTYPE(p, CALL_ANSWER)
				SET_BOOL(p, r)
			} else			
			if (func == PKSETCRYPTCALLBACKW)
			{
				int CryptoNr; GET_INT(p, CryptoNr)
				int Flags; GET_INT(p, Flags)
				PkSetCryptCallbackW(PkCryptProcW, CryptoNr, Flags);
				p = s_buff;
				SET_FUNC(p, PKSETCRYPTCALLBACKW)
				SET_CALLTYPE(p, CALL_ANSWER)
			} else
			{
				assert(0);
			}
			DWORD writeSize = p - s_buff;
			DWORD writedSize;
			if (!WriteFile(s_pipe, s_buff, writeSize, &writedSize, NULL) || writeSize != writedSize)
			{
				return -7;
			}
		} else
		if (GetLastError() != ERROR_TIMEOUT)
		{
			break;
		}
	}
	if (s_plugIn)
	{
		FreeLibrary(s_plugIn);
		s_plugIn = NULL;
	}
	CloseHandle(s_pipe);
	s_pipe = INVALID_HANDLE_VALUE;

	return 0;
}
